package XNAT::DICOM::DirectoryParser;
use XNAT::DICOM::Entity;

@ISA = ('XNAT::DICOM::Entity');

use strict;
use Time::HiRes qw( gettimeofday tv_interval );
use Carp qw(croak carp cluck confess);
use Cwd qw( abs_path getcwd );

require XNAT::DICOM::File;

my %parsed_dirs;
my %parsers_dirs;

my @csv_headers = qw(sessions start_time_seconds start_time_microseconds start_localtime dir readdir_time_seconds
										 parse_dicom_files_seconds total_dir_parsing_seconds total_seconds
										 number_of_dicom_files number_of_directory_entries	number_of_directories
										 number_of_sessions number_of_series number_of_instances dicom_files_per_second);

sub new {
  my $class = shift;
  my $self = {warnings=>{},errors=>{},subdirs=>[],csv_headers=>[@csv_headers]};
  bless $self,$class;
  $self->{directory} = shift || confess "Missing directory argument!\n";
  confess "Wrong number of arguments!\n" if @_;
  $self->{dir} = abs_path($self->{directory});
  if($parsers_dirs{$self->{dir}}) {
    warn "Wont create another parser for dir '$self->{dir}'.\n";
    $self = $parsers_dirs{$self->{dir}};
  }
  $parsers_dirs{$self->{dir}} = $self;
  # Remember to return the $self at the end!
	$self->{label} = $self->{dir};
  $self;
}

sub parse {
  my $self = shift;
  if($parsed_dirs{$self->{dir}}) {
    warn "Ignoring attempt to reparse already parsed dir '$self->{dir}'.\n";
    return;
  }
  $parsed_dirs{$self->{dir}} = $self;
  print "Going to parse '$self->{dir}'.\n" if $main::verbose > 1;
  $self->{start_time} = [gettimeofday];
	$self->{number_of_dicom_files} = 0;
	$self->{number_of_directory_entries} = 0;
	$self->{number_of_directories} = 0;
  opendir(DIR, $self->{dir}) || die "Unable to open directory '$self->{dir}' for reading!\nREASON: $!\n";
  my @files = sort(readdir(DIR));
  closedir(DIR);
	my @dicom_files;
	foreach my $file (@files) {
    next if $file eq '.' or $file eq '..';
    $file = "$self->{dir}/$file";
    $file = abs_path($file);
    if(! -e "$file" ) {
      $self->addError("'$file' was removed while attempting to parse dir '$self->{dir}'!\n");
			next;
    }
    if(-l "$file" ) {
      $self->addWarnings("Skipping symbolic link '$file'\n");
      next;
    }
    if(-f "$file" ) {
      if($file =~ m/\.dcm$|\.dcm.gz/) {
        push @dicom_files,$file;
      } elsif($file =~ m/scan_\d+_catalog\.xml/) {
        next;
      } else {
        $self->addWarnings("Skipping unknown regular file '$file'\n");
        next
      }
    } elsif( -d "$file" ) {
      $self->info("INFO: Will also parse '$file'.\n");
      my $new_parser = XNAT::DICOM::DirectoryParser->new($file);
      push @{$self->{subdirs}}, $new_parser;
      $new_parser->parse();
			$self->{number_of_dicom_files} += $new_parser->{number_of_dicom_files};
			$self->{number_of_directory_entries} += $new_parser->{number_of_directory_entries};
			$self->{number_of_directories} += $new_parser->{number_of_directories};
      next;
    } else {
      $self->addWarnings("Skipping not regular file '$file'\n");
      next;
    }
  }
  $self->{readdir_time_seconds} += tv_interval($self->{start_time});
	$self->{number_of_dicom_files} += @dicom_files;
	$self->{number_of_directory_entries} += @files;
	$self->{number_of_directories} += 1;
	if(@dicom_files) {
		print sprintf ("Read in %s directory listings for %s in %s (%s were dicom files)\n",
									 $self->{number_of_directory_entries},
									 $self->{dir},
									 $self->pretty_seconds($self->{readdir_time_seconds}),
									 $self->{number_of_dicom_files},
									) if $main::verbose > 1;
		print "0%                                              50%                                             100%\n" if 1 == $main::verbose or 2 == $main::verbose;
		print "|------------------------------------------------+-------------------------------------------------|\n" if 1 == $main::verbose or 2 == $main::verbose;
		$|=1;
		my $file_count = 0;
		my $percent = 0;
		$self->{parse_dicom_files_start_time} = [gettimeofday];
		foreach my $file (@dicom_files) {
			$file_count ++;
			my $this_percent = int($file_count*100/$self->{number_of_dicom_files});
			if ($this_percent > $percent) {
				$percent = $this_percent;
				if(1 == $percent or 50 == $percent or 100 == $percent) {
					print "|" if 1 == $main::verbose or 2 == $main::verbose;
				} else {
					print "#" if 1 == $main::verbose or 2 == $main::verbose;
				}
			}
			XNAT::DICOM::File->new($file);
		}
		print "\n" if 1 == $main::verbose or 2 == $main::verbose;

		$self->{parse_dicom_files_end_time} = [gettimeofday];
		$self->{parse_dicom_files_seconds} = tv_interval($self->{parse_dicom_files_start_time}, $self->{parse_dicom_files_end_time});
		$self->{total_dir_parsing_seconds} = tv_interval($self->{start_time}, $self->{parse_dicom_files_end_time});
    print sprintf ("Processed $file_count Files in %s, %0.2f files/second\n",
									 $self->pretty_seconds($self->{parse_dicom_files_seconds}),
									 sprintf('%0.2f',($file_count/$self->{parse_dicom_files_seconds}))
									) if 1 == $main::verbose or 2 == $main::verbose;
    print sprintf ("Total load & Parse Time: %s, %0.2f files/second\n",
									 $self->pretty_seconds($self->{total_dir_parsing_seconds}),
									 sprintf('%0.2f',($file_count/$self->{total_dir_parsing_seconds}))
									) if 1 == $main::verbose or 2 == $main::verbose;
	}
}

sub statsCSV {
  my $self = shift;
  if($self->{start_time}) {
		print "DirectoryParser Statistics:\n";
		($self->{start_time_seconds},$self->{start_time_microseconds}) = @{$self->{start_time}};
		($self->{now_sec},$self->{now_us}) = (gettimeofday);
		$self->{start_localtime} = localtime($self->{start_time_seconds});
		$self->{total_seconds} = tv_interval($self->{start_time});
		$self->{number_of_sessions} = XNAT::DICOM::Session::getAll();
		$self->{number_of_series} = XNAT::DICOM::Series::getAll();
		$self->{number_of_instances} = XNAT::DICOM::File::getAll();
		$self->{dicom_files_per_second} = $self->{number_of_dicom_files}/$self->{parse_dicom_files_seconds};
		my @session_ids;
		foreach(XNAT::DICOM::Session::getAll()) {
			my $id = $_->{label};
			push @session_ids,$_->{label};
		}
		$self->{sessions} = join(',',@session_ids);
		my @csv;
		foreach my $what ( @csv_headers ) {
			printf " - %-28s = '%s'\n",$what, $self->{$what} if $main::verbose > 1;
			my $val = $self->{$what};
			if($val =~ s{"}{""}g || $val =~ m/\s/ || $val =~ m/,/) {
				$val = "\"$val\"";
			}
			push @csv,$val;
		}
		join(',',@csv)."\n";
  } else {
    "nodata\n";
  }
}

1;

__END__
